package soundboard.player.model;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import android.content.Context;

public class BoardFactory
{
	protected static String LOC_META = "/meta";
	protected static String LOC_TEMPDIR = "/soundboard";
	protected static String BG_BASENAME = "bg";
	
	protected static Context appContext = null;
	
	public static Board readBoard(Context appContext, URL boardUrl) throws IOException
	{
		URLConnection ucon = boardUrl.openConnection();
		InputStream is = ucon.getInputStream();

		return readBoard(appContext, is);
	}

	public static Board readBoard(Context appContext, InputStream boardStream) throws IOException
	{
		if (BoardFactory.appContext==null)
			BoardFactory.appContext = appContext;

		// FIXME: fix this!
		File localFile = new File(zipPathToCachePath("download.zip"));
		if (localFile.exists())
		{
			localFile.delete();
			localFile.createNewFile();
		}
		
        BufferedInputStream bis = new BufferedInputStream(boardStream);

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(localFile));

        int current = 0;

        while ((current = bis.read()) != -1) 
        {
        	bos.write(current);
        }

		bos.close();
		bis.close();

		return readBoard(appContext, localFile);
	}

	public static Board readBoard(Context appContext, File boardFile) throws IOException
	{
		if (BoardFactory.appContext==null)
			BoardFactory.appContext = appContext;
		
		if (BoardFactory.appContext==null)
			throw new IOException("Error: application context may not be null.");

		Board board = new BoardImpl();
		board.setTitle(getBasename(boardFile.getAbsolutePath()));
		ZipFile zipFile = new ZipFile(boardFile);

		InputStream is = getInputStream(zipFile, LOC_META);
		int version = is.read();
		is.close();

		if (version>48)
			throw new IOException("Error: soundboard file version is not supported by this player: " + version);
		
		Map<String, Sound> soundMap = new HashMap<String, Sound>();
		
		Enumeration<? extends ZipEntry> entries = zipFile.entries();
		while (entries.hasMoreElements())
		{	
			Sound thisSound = null;
			
			String thisFilename = entries.nextElement().getName();
			String thisFilenameLowercase = thisFilename.toLowerCase();
			
			String thisTitle = getBasename(thisFilename);

			if (thisTitle.equals(BG_BASENAME) && 
					(thisFilenameLowercase.endsWith(".png") || 
					thisFilenameLowercase.endsWith(".jpg") || 
					thisFilenameLowercase.endsWith(".jpeg")))
				board.setBackgroundImage(getFile(zipFile, thisFilename));
			else
			{			
				if (!soundMap.containsKey(thisTitle))
				{
					thisSound = new SoundImpl();
					thisSound.setTitle(thisTitle);
					soundMap.put(thisTitle, thisSound);
				}
				else thisSound = soundMap.get(thisTitle);
				
				if (thisFilenameLowercase.endsWith(".ogg") || 
						thisFilenameLowercase.endsWith(".mp3") || 
						thisFilenameLowercase.endsWith(".wav"))
					thisSound.setAudioData(getFile(zipFile, thisFilename));
				else if (thisFilenameLowercase.endsWith(".png") || 
						thisFilenameLowercase.endsWith(".jpg") || 
						thisFilenameLowercase.endsWith(".jpeg"))
					thisSound.setIcon(getFile(zipFile, thisFilename));
			}
		}
		
		return board;
	}
	
    public static void cleanup()
    {
    	File tempDir = new File(getTempDir());
    	for (File thisFile : tempDir.listFiles())
    		thisFile.delete();
    	tempDir.delete();
    }

    private static String getBasename(String filename)
    {
		return filename.substring(filename.lastIndexOf("/")+1, filename.lastIndexOf("."));
    }
    
    private static InputStream getInputStream(ZipFile zipFile, String path) throws IOException
    {
        if (path.startsWith("/"))
                path=path.substring(1);
        return zipFile.getInputStream(zipFile.getEntry(path));
    }

    private static File getFile(ZipFile zipFile, String path) throws IOException
    {
        File tempFile = new File(zipPathToCachePath(path));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tempFile));
        BufferedInputStream bis = new BufferedInputStream(getInputStream(zipFile, path));
        int data = -1;
        while ((data=bis.read())!=-1)
                bos.write(data);
        bis.close();
        bos.close();
        return tempFile;
    }

    private static String zipPathToCachePath(String zipPath)
    {
    	return getTempDir() + "/sbd" + "-" + zipPath.replace('/', '%');
    }
    
    private static String getTempDir()
    {
    	File tempDir = new File(appContext.getCacheDir().getAbsolutePath() + LOC_TEMPDIR);
    	if (!tempDir.exists())
    		tempDir.mkdir();
    	return tempDir.getAbsolutePath();
    }
}
